02. Fields
Field
函数用于自定义字段,为其添加元数据
默认值 default
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(default='John Doe')
user = User()
print(user)
#> name='John Doe'
还可以使用 default_factory
定义一个可变的默认值
from uuid import uuid4
from pydantic import BaseModel, Field
class User(BaseModel):
id: str = Field(default_factory=lambda: uuid4().hex)
If you use
typing.Optional
, it doesn't mean that the field has a default value ofNone
!
与 Annotated
合用
Field
可以写进 Annotated
哦(不过有啥用嘛)
from uuid import uuid4
from typing_extensions import Annotated
from pydantic import BaseModel, Field
class User(BaseModel):
id: Annotated[str, Field(default_factory=lambda: uuid4().hex)]
default_factory 只能写在
Annotated
里面,而 default 无法写在里面,只能直接在外面通过赋值的形式写
字段别名 alias
定义一个 alias 有三种方法:
Field(..., alias='foo')
Field(..., validation_alias='foo')
Field(..., serialization_alias='foo')
alias 在验证和序列化时都被用到,除非用第二、三种方法分别开来
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(..., alias='username')
user = User(username='johndoe')
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True))
#> {'username': 'johndoe'}
by_alias=True
一定要显式地写出来!默认是 False
If you want to use an alias only for validation, you can use the validation_alias
parameter:
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(..., validation_alias='username')
user = User(username='johndoe')
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True))
#> {'name': 'johndoe'}
If you only want to define an alias for serialization, you can use the serialization_alias
parameter:
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(..., serialization_alias='username')
user = User(name='johndoe')
print(user)
#> name='johndoe'
print(user.model_dump(by_alias=True))
#> {'username': 'johndoe'}
数字条件约束
可以用以下参数来限制数字型字段的值:
gt
- greater thanlt
- less thange
- greater than or equal tole
- less than or equal tomultiple_of
- a multiple of the given number 给定数字的 倍数allow_inf_nan
- allow'inf'
,'-inf'
,'nan'
values
from pydantic import BaseModel, Field
class Foo(BaseModel):
positive: int = Field(gt=0)
non_negative: int = Field(ge=0)
negative: int = Field(lt=0)
non_positive: int = Field(le=0)
even: int = Field(multiple_of=2)
love_for_pydantic: float = Field(allow_inf_nan=True)
foo = Foo(
positive=1,
non_negative=0,
negative=-1,
non_positive=0,
even=2,
love_for_pydantic=float('inf'),
)
print(foo)
"""
positive=1 non_negative=0 negative=-1 non_positive=0 even=2 love_for_pydantic=inf
"""
对那些混合类型来说,使用数字约束可能会出错。使用 Annotated
来避免这种情况
from typing import Optional
from typing_extensions import Annotated
from pydantic import BaseModel, Field
class Foo(BaseModel):
positive: Optional[Annotated[int, Field(gt=0)]]
# Can error in some cases, not recommended:
non_negative: Optional[int] = Field(ge=0)
字符串条件约束
Name | Type | Description |
---|---|---|
strip_whitespace | bool | None | 是否删除前后空白 |
to_upper | bool | None | 是否转换为大写 |
to_lower | bool | None | 是否转换为小写 |
strict | bool | None | 是否使用 strict mode 去验证字符串 |
min_length | int | None| | The minimum length of the string. |
max_length | int | None | The maximum length of the string. |
pattern | str | Pattern[str] | None | 正则 pattern |
example
from pydantic import BaseModel, Field
class Foo(BaseModel):
short: str = Field(min_length=3)
long: str = Field(max_length=10)
regex: str = Field(pattern=r'^\d*$')
foo = Foo(short='foo', long='foobarbaz', regex='123')
print(foo)
#> short='foo' long='foobarbaz' regex='123'
十进制数字条件约束
max_digits
: 允许的最大数字个数,不包括小数点前 后的零。decimal_places
: 允许的最大小数位数。不包括小数后面的零。
from decimal import Decimal
from pydantic import BaseModel, Field
class Foo(BaseModel):
precise: Decimal = Field(max_digits=5, decimal_places=2)
foo = Foo(precise=Decimal('123.45'))
print(foo)
#> precise=Decimal('123.45')
dataclass 的条件约束
init
: 字段是否一定要在 dataclass 的__init__
函数中提前定义init_var
: Whether the field should be seen as an init-only field in the dataclass.kw_only
: 字段是否一定要以关键字参数的形式传参
from pydantic import BaseModel, Field
from pydantic.dataclasses import dataclass
@dataclass
class Foo:
bar: str
baz: str = Field(init_var=True)
qux: str = Field(kw_only=True)
class Model(BaseModel):
foo: Foo
model = Model(foo=Foo('bar', baz='baz', qux='qux'))
print(model.model_dump())
#> {'foo': {'bar': 'bar', 'qux': 'qux'}}
验证默认值
validate_default
决定字段的默认值是否需要被验证,默认为 False
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
age: int = Field(default='twelve', validate_default=True)
try:
user = User()
except ValidationError as e:
print(e)
"""
1 validation error for User
age
Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='twelve', input_type=str]
"""
字段展示 repr
repr
决定字段是否出现在 string representation 中,默认为 True
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(repr=True)
age: int = Field(repr=False)
user = User(name="John", age=42)
print(user)
print(user.model_dump())
#> name='John'
#> {'name': 'John', 'age': 42}
只是没有在 __repr__
的时候展示出来啦。。序列化的时候还是会有的,得用 exclude
鉴别器 Discriminator
Discriminator 用于校验联合类型中的共有字段。It takes either the name of a field or a Discriminator
instance.
from typing import Literal
from pydantic import BaseModel, Field
class Cat(BaseModel):
pet_type: Literal['cat']
age: int
class Dog(BaseModel):
pet_type: Literal['dog']
age: int
class Model(BaseModel):
pet: Cat | Dog = Field(discriminator='pet_type')
print(Model.model_validate({'pet': {'pet_type': 'cat', 'age': 12}}))
#> pet=Cat(pet_type='cat', age=12)
使用 Discriminator
instance:
from typing import Literal, Union
from typing_extensions import Annotated
from pydantic import BaseModel, Discriminator, Field, Tag
class Cat(BaseModel):
pet_type: Literal['cat']
age: int
class Dog(BaseModel):
pet_kind: Literal['dog']
age: int
def pet_discriminator(v):
if isinstance(v, dict):
return v.get('pet_type', v.get('pet_kind'))
return getattr(v, 'pet_type', getattr(v, 'pet_kind', None))
class Model(BaseModel):
pet: Union[Annotated[Cat, Tag('cat')], Annotated[Dog, Tag('dog')]] = Field(
discriminator=Discriminator(pet_discriminator)
)
print(repr(Model.model_validate({'pet': {'pet_type': 'cat', 'age': 12}})))
#> Model(pet=Cat(pet_type='cat', age=12))
print(repr(Model.model_validate({'pet': {'pet_kind': 'dog', 'age': 12}})))
#> Model(pet=Dog(pet_kind='dog', age=12))
严格模式 Strict Mode
strict
是否开启严格模式校验,默认为 True
from pydantic import BaseModel, Field
class User(BaseModel):
name: str = Field(strict=True)
age: int = Field(strict=False)
user = User(name='John', age='42') # The `age` field is not validated in the strict mode. Therefore, it can be assigned a string.
print(user)
#> name='John' age=42
不变性 Immutability
frozen
模仿了 frozen dataclass 的行为,避免在初始化后 model 的值被修改
from pydantic import BaseModel, Field, ValidationError
class User(BaseModel):
name: str = Field(frozen=True)
age: int
user = User(name='John', age=42)
try:
user.name = 'Jane'
except ValidationError as e:
print(e)
"""
1 validation error for User
name
Field is frozen [type=frozen_field, input_value='Jane', input_type=str]
"""
排除 Exclude
exclude
能让 model 在导出(export,即序列化)时忽略字段
from pydantic import BaseModel, Field
class User(BaseModel):
name: str
age: int = Field(exclude=True)
user = User(name='John', age=42)
print(user.model_dump())
#> {'name': 'John'}
废弃的字段 Deprecated
deprecated
标记过期字段,这会导致两种结果:
- a runtime deprecation warning emitted when accessing the field.
"deprecated": true
being set in the generated JSON schema.
deprecated
可以是:
- 一个字符串,标明废弃信息
- An instance of the
warnings.deprecated
decorator (or thetyping_extensions
backport) - 一个布尔值
from typing_extensions import Annotated
from pydantic import BaseModel, Field
class Model(BaseModel):
deprecated_field: Annotated[int, Field(deprecated='This is deprecated')]
print(Model.model_json_schema()['properties']['deprecated_field'])
#> {'deprecated': True, 'title': 'Deprecated Field', 'type': 'integer'}
from typing_extensions import Annotated, deprecated
from pydantic import BaseModel, Field
class Model(BaseModel):
deprecated_field: Annotated[int, deprecated('This is deprecated')]
# Or explicitly using `Field`:
alt_form: Annotated[int, Field(deprecated=deprecated('This is deprecated'))]
from typing_extensions import Annotated
from pydantic import BaseModel, Field
class Model(BaseModel):
deprecated_field: Annotated[int, Field(deprecated=True)]
print(Model.model_json_schema()['properties']['deprecated_field'])
#> {'deprecated': True, 'title': 'Deprecated Field', 'type': 'integer'}
自定义 JSON Schema
Some field parameters are used exclusively to customize the generated JSON schema. The parameters in question are:
title
description
examples
json_schema_extra
Read more about JSON schema customization / modification with fields in the [[Customizing JSON Schema]] section of the JSON schema docs.
computed_field
装饰器
computed_field
装饰器能够将 property
or cached_property
装饰的字段也囊括进序列化中
This can be useful for fields that are computed from other fields, or for fields that are expensive to computed (and thus, are cached).
from pydantic import BaseModel, computed_field
class Box(BaseModel):
width: float
height: float
depth: float
@computed_field
def volume(self) -> float:
return self.width * self.height * self.depth
b = Box(width=1, height=2, depth=3)
print(b.model_dump())
#> {'width': 1.0, 'height': 2.0, 'depth': 3.0, 'volume': 6.0}
As with regular fields, computed fields can be marked as being deprecated:
from typing_extensions import deprecated
from pydantic import BaseModel, computed_field
class Box(BaseModel):
width: float
height: float
depth: float
@computed_field
@deprecated("'volume' is deprecated")
def volume(self) -> float:
return self.width * self.height * self.depth